#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/resource.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "iscsi.h"
#include "iser.h"
#include "list.h"
#include "core.h"
#include "rdma_event.h"

unsigned long pagesize, pageshift;

static int ep_fd;
static LIST_HEAD(events_list);

static __thread int private_ep_fd;
static __thread struct list_head private_events_list;
static int need_epoll_wait[256] = {0};

int get_ep_fd()
{
        if (core_self())
                return private_ep_fd;
        else
                return ep_fd;
}

struct list_head *get_events_list()
{
        if (core_self())
                return &private_events_list;
        else
                return &events_list;
}

void set_epoll_wait(int hash)
{
        DWARN("the epoll wait need exec %d\n", hash);
        need_epoll_wait[hash] = 1;
}

void clear_epoll_wait(int hash)
{
        DWARN("the epoll wait need clear %d\n", hash);
       need_epoll_wait[hash] = 0;
}

int event_add(int fd, int events, event_handle_t handler, void *data, void *private_mem)
{
        struct epoll_event ev;
        struct event_data *tev;
        int ret;

        ret = ymalloc((void **)&tev, sizeof(*tev));
        if (ret)
                return -ENOMEM;

        tev->data = data;
        tev->handler = handler;
        tev->fd = fd;
        tev->private_mem = private_mem;

        memset(&ev, 0, sizeof(ev));
        ev.events = events;
        ev.data.ptr = tev;
        ret = epoll_ctl(get_ep_fd(), EPOLL_CTL_ADD, fd, &ev);
        if (ret) {
                DERROR("Cannot add fd, %m\n");
                free(tev);
        } else
                list_add(&tev->e_list, get_events_list());

        return ret;
}

static struct event_data *event_lookup(int fd)
{
        struct event_data *tev;

        list_for_each_entry(tev, get_events_list(), e_list) {
                if (tev->fd == fd)
                        return tev;
        }
        return NULL;
}

static int event_need_refresh;

void event_del(int fd)
{
        struct event_data *tev;
        int ret;

        tev = event_lookup(fd);
        if (!tev) {
                DERROR("Cannot find event %d\n", fd);
                return;
        }

        ret = epoll_ctl(get_ep_fd(), EPOLL_CTL_DEL, fd, NULL);
        if (ret < 0)
                DERROR("fail to remove epoll event, %s\n", strerror(errno));

        list_del(&tev->e_list);
        free(tev);

        event_need_refresh = 1;
}

int event_modify(int fd, int events)
{
        struct epoll_event ev;
        struct event_data *tev;

        tev = event_lookup(fd);
        if (!tev) {
                DERROR("Cannot find event %d\n", fd);
                return -EINVAL;
        }

        memset(&ev, 0, sizeof(ev));
        ev.events = events;
        ev.data.ptr = tev;

        return epoll_ctl(get_ep_fd(), EPOLL_CTL_MOD, fd, &ev);
}

void *event_loop(void *_arg)
{
        int nevent, i, sched_remains, timeout;
        struct epoll_event events[1024];
        struct event_data *tev;

        (void) _arg;
retry:
        sched_remains = iser_exec_scheduled();
        timeout = sched_remains ? 0 : -1;

        nevent = epoll_wait(get_ep_fd(), events, ARRAY_SIZE(events), timeout);
        if (nevent < 0) {
                if (errno != EINTR) {
                        DERROR("%m\n");
                        exit(1);
                }
        } else if (nevent) {
                for (i = 0; i < nevent; i++) {
                        tev = (struct event_data *) events[i].data.ptr;
                        tev->handler(tev->fd, tev->type, events[i].events, tev->data, NULL);

                        if (event_need_refresh) {
                                event_need_refresh = 0;
                                goto retry;
                        }
                }
        }


        if (rdma_running)
                goto retry;

        return NULL;
}

void iser_acceptor(void)
{
        int nevent,i;
	struct epoll_event events[1024];
	struct event_data *tev;
	//DWARN("new vol login coming  %d\n", hash);

	nevent = epoll_wait(get_ep_fd(), events, ARRAY_SIZE(events), 0);
	if (unlikely(nevent < 0)) {
		if (errno != EINTR) {
			DERROR("%m\n");
			YASSERT(0);
		}
	} else if (unlikely(nevent)) {
		for (i = 0; i < nevent; i++) {
			tev = (struct event_data *) events[i].data.ptr;
			tev->handler(tev->fd, tev->fd, events[i].events, tev->data, tev->private_mem);

			if (event_need_refresh) {
				event_need_refresh = 0;
			}
		}
	}

}

void event_polling(int hash, void *dev)
{
        (void)hash;
        /*sched_remains = iser_exec_scheduled();
        timeout = sched_remains ? 0 : tmo; */
        iser_exec_scheduled();

        if (likely(dev))
                iser_poll_cq((struct iser_device *)dev, 256);

        iser_exec_scheduled();
}

int event_init()
{
        int ret;

        if (core_self()) {
                INIT_LIST_HEAD(&private_events_list);

                iser_dev_init();
                iser_sched_init();

                private_ep_fd = epoll_create(ISCSI_NR_EPOLL_FD);
                if (private_ep_fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }
        } else {
                ep_fd = epoll_create(ISCSI_NR_EPOLL_FD);
                if (ep_fd < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return ret;

}
